﻿namespace LogManager.Core.Services.Report
{
    using LogManager.Core.Models;
    using LogManager.Core.Services.Location;
    using LogManager.Utils.Helpers;
    using System;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.IO;
    using System.Linq;
    using System.Net.Mail;
    using System.Net.Mime;
    using System.Text;
    using System.Threading.Tasks;
    using cssMail = PreMailer.Net;

    public class ReportService : IReportService
    {
        // outlook ignores some inline style on images
        const string IMAGES_WIDTH_OUTLOOK = " width='16' ";

        const string IMG_ACCEPT = "IMG_ACCEPT";
        const string IMG_ERROR = "IMG_ERROR";
        const string IMG_WARHOUSE = "IMG_WARHOUSE";
        const string IMG_PLATEFORM = "IMG_PLATEFORM";
        const string IMG_SERVER = "IMG_SERVER";
        const string IMG_FOLDER = "IMG_FOLDER";
        const string IMG_NETWORKFOLDER = "IMG_NETWORKFOLDER";
        const string IMG_ZIP_ARCHIVE = "IMG_ZIP_ARCHIVE";

        private static string BuildWarehouse(LogArchivingResult res, IWarehouse warehouse)
        {
            return "<div class='node warehouse'>" + BuildStateImage(res.HasSucceeded(warehouse)) +
                "<span class='icon'><img " + IMAGES_WIDTH_OUTLOOK + " src='cid:" + IMG_WARHOUSE + "' /></span>" + warehouse.Name + "</div>";
        }

        private static string BuildPlateform(LogArchivingResult res, IPlateform plateform)
        {
            return "<div class='node plateform'>" + BuildStateImage(res.HasSucceeded(plateform)) +
                "<span class='icon'><img " + IMAGES_WIDTH_OUTLOOK + " src='cid:" + IMG_PLATEFORM + "' /></span>" + plateform.Name + "</div>";
        }

        private static string BuildServer(LogArchivingResult res, IServer server)
        {
            return "<div class='node server'>" + BuildStateImage(res.HasSucceeded(server)) +
                "<span class='icon'><img " + IMAGES_WIDTH_OUTLOOK + " src='cid:" + IMG_SERVER + "' /></span>" + server.Name + " (" + server.RetentionHours + " h)</div>";
        }

        private static string BuildLocation(LogArchivingResult res, ILocation location)
        {
            return "<div class='node location'>" + BuildStateImage(res.HasSucceeded(location)) +
                "<span class='icon'><img " + IMAGES_WIDTH_OUTLOOK + " src='cid:" + IMG_FOLDER + "' /></span>" + location.Name + "</div>";
        }

        private static string BuildNetworkFolders(LocationArchivingProgress result)
        {
            var output = "<div class='node network-folder'>" + BuildStateImage(result.SourceResult.Succeeded) +
                "<span class='icon'><img " + IMAGES_WIDTH_OUTLOOK + " src='cid:" + IMG_NETWORKFOLDER + "' /></span>" + result.Location.FullPathSource + "</div>";
            if (!result.SourceResult.Succeeded)
            {
                output += "<div class='node error'>" + result.SourceResult.Message + " </div>";
            }

            output += "<div class='node network-folder'>" + BuildStateImage(result.DestinationResult.Succeeded) +
                "<span class='icon'><img " + IMAGES_WIDTH_OUTLOOK + " src='cid:" + IMG_NETWORKFOLDER + "' /></span>" + result.Location.FullPathDestination + "</div>";
            if (!result.DestinationResult.Succeeded)
            {
                output += "<div class='node error'>" + result.DestinationResult.Message + " </div>";
            }

            return output;
        }

        private static string BuildFiles(LocationArchivingProgress result)
        {
            if (result.LogFilesSearchResult == null || !result.LogFilesSearchResult.Succeeded)
            {
                return "<div class='node error'>File search failed : " + result.LogFilesSearchResult.Message + " </div>";
            }

            var output = "<div class='ul'><div class='node files-moved'>Files moved : " + result.LogFilesSearchResult.Files.Where(p => p.Error == null).Count() + "</div>";

            foreach (var logfile in result.LogFilesSearchResult.Files)
            {
                output += "<div class='node logfile'>" + BuildStateImage(logfile.Error == null) +
                    "<span class='icon'><img " + IMAGES_WIDTH_OUTLOOK + " src='cid:" + IMG_ZIP_ARCHIVE + "' /></span>" + logfile.LocationRelativePath + "</div>";
                if (logfile.Error != null)
                {
                    output += "<div class='node error'>Error:" + logfile.Error.Message + "<br/>CurrentStep:" + logfile.LogFileStep + " </div>";
                }
            }
            output += "</div>";
            return output;
        }

        private static string BuildStateImage(bool succeeded)
        {
            if (succeeded)
                return "<span class='state'><img " + IMAGES_WIDTH_OUTLOOK + " src='cid:" + IMG_ACCEPT + "'/></span>";
            else
                return "<span class='state'><img " + IMAGES_WIDTH_OUTLOOK + " src='cid:" + IMG_ERROR + "'/></span>";
        }

        private static string BuildSummary(LogArchivingResult logArchivingResult)
        {
            var sb = new StringBuilder();

            sb.AppendLine(@"<div class='summary'>
                            <div class='ul'>
                                <div>Result : " + BuildStateImage(logArchivingResult.Succeeded) + @"</div>
                                <div>Total files : " + logArchivingResult.TotalFiles + @"</div>
                                <div>Total files moved : " + logArchivingResult.TotalFilesMoved + @"</div>
                            </div>
                            </div>");
            return sb.ToString();
        }

        private static string BuildSubject(LogArchivingResult logArchivingResult)
        {
            var output = "Log manager results : ";

            if (logArchivingResult.Succeeded)
                output += " ✔ Success";
            else
                output += " ✘ Errors";

            return output;
        }

        private static string BuildReportBody(LogArchivingResult logArchivingResult)
        {
            var sb = new StringBuilder();

            sb.AppendLine(@"<!DOCTYPE html>
                            <html>
                            <head>
                            <style type='text/css'>
                                body                    {font-size:12px; font-family:'Segoe UI';}
                                .head-title             {border-bottom: 1px solid; font-size: 16px;}
                                .node                   {margin-top:3px;}
                                .node .icon             {border:2px solid #fff;width:16px; height:16px;margin:5px;vertical-align:top;}
                                .node .state            {width:16px; height:16px;vertical-align:top;}
                                .ul                     {padding-left:0px;margin-left:20px; list-style:none;}
                                .li                     {padding-left:0px;margin-left:20px; list-style:none;}
                                .folder-details         {list-style:none;}
                                .error                  {color:#ff0000;}
                            </style>
                            </head>
                            <body>");

            sb.AppendLine("<div class='head-title'>Summary</div>");
            sb.AppendLine(BuildSummary(logArchivingResult));
            sb.AppendLine("<div class='head-title'>Details</div>");

            sb.AppendLine("<div class='ul'>");
            foreach (var warehouse in logArchivingResult.Configuration.Warehouses)
            {
                sb.AppendLine(BuildWarehouse(logArchivingResult, warehouse));
                sb.AppendLine("<div class='ul'>");
                foreach (var plateform in warehouse.Plateforms)
                {
                    sb.AppendLine(BuildPlateform(logArchivingResult, plateform));
                    sb.AppendLine("<div class='ul'>");
                    foreach (var server in plateform.Servers)
                    {
                        sb.AppendLine(BuildServer(logArchivingResult, server));
                        sb.AppendLine("<div class='ul'>");

                        foreach (var location in server.Locations)
                        {
                            sb.AppendLine(BuildLocation(logArchivingResult, location));

                            var result = logArchivingResult.GetLocationResult(location);
                            sb.AppendLine("<div class='ul' class='folder-details'>");
                            sb.AppendLine(BuildNetworkFolders(result));

                            if (result.SourceResult.Succeeded && result.DestinationResult.Succeeded)
                            {
                                sb.AppendLine(BuildFiles(result));
                            }

                            sb.AppendLine("</div>");
                        }
                        sb.AppendLine("</div>");
                    }
                    sb.AppendLine("</div>");
                }
                sb.AppendLine("</div>");
            }
            sb.AppendLine("</div>");

            sb.AppendLine("</body>");

            return cssMail.PreMailer.MoveCssInline(sb.ToString()).Html;
        }

        private static SmtpClient CreateSmtpClient(Models.Smtp.ISmtpSettings smtpSettings)
        {
            SmtpClient clientObj = new SmtpClient();
#if DEBUG
            clientObj.DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory;
            clientObj.PickupDirectoryLocation = @"c:\email";
#else
            var serverSettings = smtpSettings.SmtpServer;
            clientObj.Host = serverSettings.Host;
            clientObj.Port = serverSettings.Port;
            clientObj.Credentials = new NetworkCredential(serverSettings.SmtpLogin, serverSettings.SmtpPass);
            clientObj.EnableSsl = serverSettings.EnableSsl;
#endif
            return clientObj;
        }

        private static void MapAllAttachements(MailMessage mail)
        {
            AddAttachementToMail(mail, CreateAttachement(Resources.Images.accept, IMG_ACCEPT));
            AddAttachementToMail(mail, CreateAttachement(Resources.Images.error, IMG_ERROR));
            AddAttachementToMail(mail, CreateAttachement(Resources.Images.home, IMG_WARHOUSE));
            AddAttachementToMail(mail, CreateAttachement(Resources.Images.plateform, IMG_PLATEFORM));
            AddAttachementToMail(mail, CreateAttachement(Resources.Images.server, IMG_SERVER));
            AddAttachementToMail(mail, CreateAttachement(Resources.Images.folder, IMG_FOLDER));
            AddAttachementToMail(mail, CreateAttachement(Resources.Images.networkFolder, IMG_NETWORKFOLDER));
            AddAttachementToMail(mail, CreateAttachement(Resources.Images.zip, IMG_ZIP_ARCHIVE));
        }

        private static void AddAttachementToMail(MailMessage mail, Attachment attachement)
        {
            if (mail.Body.Contains(attachement.ContentId))
                mail.Attachments.Add(attachement);
        }

        private static Attachment CreateAttachement(Bitmap image, string id)
        {
            var ms = new MemoryStream();
            image.Save(ms, ImageFormat.Png);
            ms.Position = 0;
            Attachment attachment = new Attachment(ms, new ContentType(MediaTypeNames.Image.Gif));
            attachment.ContentId = id;
            return attachment;
        }

        public static SimpleServiceResult SendReportStatic(LogArchivingResult logArchivingResult)
        {
            var smtpSettings = logArchivingResult.Configuration.Settings.SmtpSettings;

            SmtpClient clientObj = CreateSmtpClient(smtpSettings);
            MailMessage oMail = new MailMessage();
            oMail.From = new MailAddress(smtpSettings.From.Address, smtpSettings.From.DisplayName);
            smtpSettings.To.MailAddresses.ForEach(p => oMail.To.Add(p));
            oMail.IsBodyHtml = true;
            oMail.Subject = BuildSubject(logArchivingResult);

            try
            {
                oMail.Body = BuildReportBody(logArchivingResult);
                MapAllAttachements(oMail);
                clientObj.Send(oMail);
                oMail.Attachments.ToList().ForEach(p => p.Dispose());

                return SimpleServiceResult.CreateSuccess("Email sent successfully");
            }
            catch (Exception ex)
            {
                return SimpleServiceResult.CreateFailure("Failed to send email \n" + ex.Message, ex);
            }
        }

        public SimpleServiceResult SendReport(LogArchivingResult logArchivingResult)
        {
            return ReportService.SendReportStatic(logArchivingResult);
        }

        public async Task<SimpleServiceResult> SendReportAsync(LogArchivingResult logArchivingResult)
        {
            return await Task.Factory.StartNew<SimpleServiceResult>(() => ReportService.SendReportStatic(logArchivingResult)).ConfigureAwait(false);
        }

    }
}
